﻿using System;
using System.Collections.Generic;
using System.Linq;


namespace FireBus.Contracts
{
	[Serializable]
	public class ExtensionMetadata
	{
		private readonly ExtensionName _Name;

		private readonly IList<TypeName> _ListensToEvents;

		private readonly IList<TypeName> _NotifiesAboutEvents;

		private readonly IList<RequestMetadata> _ListensToRequests;


		public ExtensionName Name
		{
			get { return _Name; }
		}


		public ExtensionMetadata(
			ExtensionName name,
			IEnumerable<TypeName> listensToEvents,
			IEnumerable<TypeName> notifiesAboutEvents,
			IEnumerable<RequestMetadata> listensToRequests)
		{
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			if (listensToEvents == null)
			{
				throw new ArgumentNullException("listensToEvents");
			}
			if (notifiesAboutEvents == null)
			{
				throw new ArgumentNullException("notifiesAboutEvents");
			}
			if (listensToRequests == null)
			{
				throw new ArgumentNullException("listensToRequests");
			}
			_Name = name;
			_ListensToEvents = new List<TypeName>(listensToEvents);
			_NotifiesAboutEvents = new List<TypeName>(notifiesAboutEvents);
			_ListensToRequests = new List<RequestMetadata>(listensToRequests);
		}


		public Boolean CanListenToEvent(TypeName eventTypeName)
		{
			return Enumerable.Contains(
				_ListensToEvents, eventTypeName);
		}

		public Boolean CanNotifyAboutEvent(TypeName eventTypeName)
		{
			return Enumerable.Contains(
				_NotifiesAboutEvents, eventTypeName);
		}

		public Boolean CanListenToRequest(TypeName requestTypeName, TypeName responseTypeName)
		{
			var requestMetadata = new RequestMetadata(requestTypeName, responseTypeName);
			return Enumerable.Contains(
				_ListensToRequests, requestMetadata);
		}

		public static ExtensionMetadata FromType(
			ExtensionName name, Type extensionType)
		{
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			if (extensionType == null)
			{
				throw new ArgumentNullException("extensionType");
			}
			var listens = new List<TypeName>();
			var fires = new List<TypeName>();
			var requestListeners = new List<RequestMetadata>();

			var eventListenerInterfaceType = typeof(IEventListener<>);
			var requestListenerInterfaceType = typeof(IRequestListener<,>);
			var eventNotifierInterfaceType = typeof (IEventNotifier<>);
				
			foreach (var interfaceType in extensionType.GetInterfaces())
			{
				if (!interfaceType.IsGenericType)
				{
					continue;
				}
				var genericInterfaceDefinition = interfaceType.GetGenericTypeDefinition();
				if (eventListenerInterfaceType == genericInterfaceDefinition ||
					eventNotifierInterfaceType == genericInterfaceDefinition)
				{
					var eventType = interfaceType.GetGenericArguments()[0];
					if (eventNotifierInterfaceType == genericInterfaceDefinition)
					{
						fires.Add(eventType);
					}
					else
					{
						listens.Add(eventType);
					}
				}
				else if(requestListenerInterfaceType == genericInterfaceDefinition)
				{
					var arguments = interfaceType.GetGenericArguments();
					var requestType = arguments[0];
					var responseType = arguments[1];
					requestListeners.Add(
						new RequestMetadata(
							requestType.AssemblyQualifiedName,
							responseType.AssemblyQualifiedName));
				}
			}

			return new ExtensionMetadata(
				name,
				listens,
				fires,
				requestListeners);
		}
	}
}
